package org.example.inventory.util;




import org.example.inventory.config.DruidConfig;
import org.example.inventory.exception.JdbcException;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class JdbcUtil {

    // 线程本地变量：用于存储与当前线程绑定的数据库连接，确保事务操作在同一连接中执行
    private static final ThreadLocal<Connection> THREAD_LOCAL = new ThreadLocal<>();

    // 数据源对象：使用Druid连接池实现，通过配置类获取已初始化的连接池实例
    private static final DataSource DATA_SOURCE = DruidConfig.getDataSource();


    /**
     * 获取连接 (此方法不对外暴露)
     * @return
     * @throws SQLException
     */
    private static Connection getConnection() throws SQLException {
        Connection conn = THREAD_LOCAL.get();
        if (conn == null || conn.isClosed()) {
            conn = DATA_SOURCE.getConnection();
            THREAD_LOCAL.set(conn);
        }
        return conn;
    }
    /**
     * 开启事务（默认隔离级别）
     */
    public static void beginTransaction() throws SQLException {
        beginTransaction(Connection.TRANSACTION_READ_COMMITTED);
    }

    /**
     * 开启指定隔离级别的事务
     */
    public static void beginTransaction(int isolationLevel) throws SQLException {
        Connection conn = getConnection();
        if (conn.getAutoCommit()) {
            conn.setAutoCommit(false);
            conn.setTransactionIsolation(isolationLevel);
        }
    }

    /**
     * 提交事务并释放连接
     */
    public static void commitTransaction() {
        Connection conn = THREAD_LOCAL.get();
        if (conn != null) {
            try {
                conn.commit();
            } catch (SQLException e) {
                throw new JdbcException("事务提交失败", e);
            } finally {
                closeConnection();
            }
        }
    }

    /**
     * 回滚事务并释放连接
     */
    public static void rollbackTransaction() {
        Connection conn = THREAD_LOCAL.get();
        if (conn != null) {
            try {
                conn.rollback();
            } catch (SQLException e) {
                throw new JdbcException("事务回滚失败", e);
            } finally {
                closeConnection();
            }
        }
    }

    /**
     * 在事务中执行操作（自动提交/回滚）
     * @param action 事务逻辑回调
     * @return 事务执行结果
     */
    public static <T> T doInTransaction(TransactionCallback<T> action) {
        Connection conn = null;
        try {
            conn = getConnection();
            // 设置事务参数
            conn.setAutoCommit(false);
            // 进行回调, 此时并不明确事务内要做什么, 因为这里的executor是抽象的
            T result = action.execute(conn);
            conn.commit();
            return result;
        }catch (SQLException e) {
            // 回滚事务
            if (conn != null) {
                try {
                    conn.rollback();
                } catch (SQLException ex) {
                    throw new JdbcException("事务回滚失败", ex);
                }
            }
            throw new JdbcException("事务执行失败", e);
        } finally {
            // 恢复原始自动提交状态并释放连接
            if (conn != null) {
                closeConnection();
            }
        }
    }


    private static void closeConnection() {
        Connection conn = THREAD_LOCAL.get();
        if (conn != null) {
            try {
                if (!conn.isClosed()) {
                    conn.close();
                }
            } catch (SQLException e) {
                logError("连接关闭失败", e);
            } finally {
                THREAD_LOCAL.remove();
            }
        }
    }



    /**
     * 设置预编译参数
     * 入参1: 预编译执行对象ps
     * 入参2: 动态的Object类型参数params
     */
    private static void setParameters(PreparedStatement ps, Object... params)
            throws SQLException {
        // 如果params数组为null,直接返回,不填充参数
        if (params == null) return;
        // 遍历params数组
        for (int i = 0; i < params.length; i++) {
            // 取出每个params参数
            Object param = params[i];
            // 填充参数
            ps.setObject(i + 1, param);
        }
    }

    /**
     * 封装执行DML操作的方法（INSERT/UPDATE/DELETE）
     * 入参1：SQL语句
     * 入参2：动态参数
     */
    public static int executeUpdate(String sql, Object... params) {
        // 获取连接, 预编译SQL语句
        try (Connection conn = getConnection();
             PreparedStatement ps = conn.prepareStatement(sql)) {
            // 调用方法填充sql参数
            setParameters(ps, params);
            // 返回执行结果
            return ps.executeUpdate();
        } catch (SQLException e) {
            // 如果发生Sql异常, 抛出自定义的Jdbc异常
            throw new JdbcException("执行更新失败", e);
        }
    }

    /**
     * 执行查询并返回单个结果
     */
    public static <T> T executeQuerySingle(String sql, ResultSetHandler<T> handler, Object... params) {
        try (Connection conn = getConnection();
             PreparedStatement ps = conn.prepareStatement(sql)) {

            setParameters(ps, params);
            try (ResultSet rs = ps.executeQuery()) {
                // 函数式接口的作用在这里是为了实现回调
                return rs.next() ? handler.handle(rs) : null;
            }
        } catch (SQLException e) {
            throw new JdbcException("执行查询失败", e);
        }
    }

    /**
     * 执行查询返回结果列表
     */
    public static <T> List<T> executeQueryList(String sql, ResultSetHandler<T> handler, Object... params) {
        List<T> list = new ArrayList<>();
        try (Connection conn = getConnection();
             PreparedStatement ps = conn.prepareStatement(sql)) {

            setParameters(ps, params);
            try (ResultSet rs = ps.executeQuery()) {
                while (rs.next()) {
                    list.add(handler.handle(rs));
                }
            }
            return list;

        } catch (SQLException e) {
            throw new JdbcException("执行查询失败", e);
        }
    }

    private static void logDebug(String message) {
        System.out.println("[DEBUG] " + message);
    }

    private static void logError(String message, Throwable e) {
        System.err.println("[ERROR] " + message);
        e.printStackTrace();
    }
}